<?php
/**
 * @author all@cosmos-framework.org
 * @copyright (c) 2013 cosmos-framework.org
 * @license http://www.opensource.org/licenses/mit-license.php
 * @link http://cosmos-framework.org/
 * @package cosmos.framework
 * @version 2.0
 */
namespace Cosmos;

class Dispatcher {

    private $_modules = array();

    /**
     * @example Dispatcher::hook('.*', 'Template::hook')
     * @example Dispatcher()
     */
    public function __invoke() {

        try {
            $this->_invoke();
        } catch (Exception $e) {
            self::_fail($e);
        } catch (\Exception $e) {
            self::_fail(new Exception($e->getMessage(), 500, $e));
        }
    }

    /**
     * @uses $_SERVER.DOCUMENT_ROOT
     * @uses $_SERVER.REQUEST_URI
     * @uses $_SERVER.PATH_INFO
     */
    public static function pathinfo() {

        $p = explode('/', ltrim(URL::path(), '/'));

        $response = '';
        while ($_ = array_shift($p)) {
            $response .= '/' . $_;
            if (is_file($_SERVER['DOCUMENT_ROOT'] . $response)) {
                $_SERVER['REQUEST_URI'] = $response;
                $_SERVER['PATH_INFO'] = $p;
                break;
            }
        }

        return 1;
    }

    /**
     * @param string $pttr
     * @param callable $module
     */
    public function hook($pttr, callable $module) {

        $this->_modules[$pttr][] = $module;
    }

    /**
     * fail response.
     */
    private static function _fail(Exception $e) {

        http_response_code($e->code());
        echo $e->message();
    }

    /**
     * @param string $pttr
     * @return boolean
     */
    private static function _match($pttr) {

        return preg_match('/' . ltrim($pttr, '!') . '/', URL::path()) xor preg_match('/^!/', $pttr);
    }

    /**
     * @access private
     */
    private function _invoke() {

        // hook response.
        foreach ($this->_modules as $pttr => $_) {
            if (self::_match($pttr)) {
                foreach ($_ as $module) {
                    if (!call_user_func($module)) {
                        break 2;
                    }
                }
            }
        }
    }

}
